// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "cc/test/layer_tree_host_remote_for_testing.h"

#include "base/memory/ptr_util.h"
#include "cc/animation/animation_host.h"
#include "cc/blimp/compositor_proto_state.h"
#include "cc/blimp/compositor_state_deserializer.h"
#include "cc/blimp/remote_compositor_bridge.h"
#include "cc/layers/layer.h"
#include "cc/proto/compositor_message.pb.h"
#include "cc/test/fake_image_serialization_processor.h"
#include "cc/test/remote_client_layer_factory.h"
#include "cc/trees/layer_tree_host_client.h"
#include "cc/trees/layer_tree_host_in_process.h"
#include "cc/trees/mutator_host.h"

namespace cc {

class LayerTreeHostRemoteForTesting::RemoteCompositorBridgeImpl
    : public RemoteCompositorBridge {
public:
    RemoteCompositorBridgeImpl(
        scoped_refptr<base::SingleThreadTaskRunner> compositor_main_task_runner)
        : RemoteCompositorBridge(std::move(compositor_main_task_runner))
    {
    }

    ~RemoteCompositorBridgeImpl() override = default;

    void SetRemoteHost(LayerTreeHostRemoteForTesting* layer_tree_host_remote)
    {
        DCHECK(layer_tree_host_remote);
        layer_tree_host_remote_ = layer_tree_host_remote;
    }

    // RemoteCompositorBridge implementation.
    void BindToClient(RemoteCompositorBridgeClient* client) override
    {
        DCHECK(!client_);
        client_ = client;
    }
    void ScheduleMainFrame() override
    {
        layer_tree_host_remote_->RemoteHostNeedsMainFrame();
    }
    void ProcessCompositorStateUpdate(
        std::unique_ptr<CompositorProtoState> compositor_proto_state) override
    {
        layer_tree_host_remote_->ProcessRemoteCompositorUpdate(
            std::move(compositor_proto_state));
    }

private:
    LayerTreeHostRemoteForTesting* layer_tree_host_remote_ = nullptr;
    RemoteCompositorBridgeClient* client_ = nullptr;
};

class LayerTreeHostRemoteForTesting::LayerTreeHostInProcessClient
    : public LayerTreeHostClient {
public:
    LayerTreeHostInProcessClient(
        LayerTreeHostRemoteForTesting* layer_tree_host_remote)
        : layer_tree_host_remote_(layer_tree_host_remote)
    {
    }

    ~LayerTreeHostInProcessClient() override = default;

    void WillBeginMainFrame() override { }
    void BeginMainFrame(const BeginFrameArgs& args) override
    {
        layer_tree_host_remote_->BeginRemoteMainFrame();
    }
    void BeginMainFrameNotExpectedSoon() override { }
    void DidBeginMainFrame() override { }
    void UpdateLayerTreeHost() override { }
    void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta,
        const gfx::Vector2dF& outer_delta,
        const gfx::Vector2dF& elastic_overscroll_delta,
        float page_scale,
        float top_controls_delta) override
    {
        layer_tree_host_remote_->compositor_state_deserializer_
            ->ApplyViewportDeltas(inner_delta, outer_delta,
                elastic_overscroll_delta, page_scale,
                top_controls_delta);
    }
    void RequestNewCompositorFrameSink() override
    {
        layer_tree_host_remote_->client()->RequestNewCompositorFrameSink();
    }
    void DidInitializeCompositorFrameSink() override
    {
        layer_tree_host_remote_->client()->DidInitializeCompositorFrameSink();
    }
    void DidFailToInitializeCompositorFrameSink() override
    {
        layer_tree_host_remote_->client()->DidFailToInitializeCompositorFrameSink();
    }
    void WillCommit() override { }
    void DidCommit() override { }
    void DidCommitAndDrawFrame() override
    {
        layer_tree_host_remote_->client()->DidCommitAndDrawFrame();
    }
    void DidReceiveCompositorFrameAck() override
    {
        layer_tree_host_remote_->client()->DidReceiveCompositorFrameAck();
    }
    void DidCompletePageScaleAnimation() override
    {
        NOTREACHED() << "The remote mode doesn't support sending animations";
    }

private:
    LayerTreeHostRemoteForTesting* layer_tree_host_remote_;
};

// static
std::unique_ptr<RemoteCompositorBridge>
LayerTreeHostRemoteForTesting::CreateRemoteCompositorBridge(
    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
{
    return base::MakeUnique<RemoteCompositorBridgeImpl>(
        std::move(main_task_runner));
}

// static
std::unique_ptr<LayerTreeHostRemoteForTesting>
LayerTreeHostRemoteForTesting::Create(
    LayerTreeHostClient* client,
    MutatorHost* mutator_host,
    LayerTreeSettings const* settings,
    TaskGraphRunner* task_graph_runner,
    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
    scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner)
{
    std::unique_ptr<FakeImageSerializationProcessor>
        image_serialization_processor = base::MakeUnique<FakeImageSerializationProcessor>();

    LayerTreeHostRemote::InitParams params;
    params.client = client;
    params.main_task_runner = main_task_runner;
    params.mutator_host = mutator_host;
    params.remote_compositor_bridge = CreateRemoteCompositorBridge(main_task_runner);
    params.engine_picture_cache = image_serialization_processor->CreateEnginePictureCache();
    params.settings = settings;

    std::unique_ptr<LayerTreeHostRemoteForTesting> layer_tree_host = base::WrapUnique(new LayerTreeHostRemoteForTesting(&params));
    layer_tree_host->Initialize(task_graph_runner, main_task_runner,
        impl_task_runner,
        std::move(image_serialization_processor));
    return layer_tree_host;
}

LayerTreeHostRemoteForTesting::LayerTreeHostRemoteForTesting(InitParams* params)
    : LayerTreeHostRemote(params)
    , layer_tree_host_in_process_client_(
          base::MakeUnique<LayerTreeHostInProcessClient>(this))
{
}

LayerTreeHostRemoteForTesting::~LayerTreeHostRemoteForTesting()
{
    animation_host_->SetMutatorHostClient(nullptr);
    compositor_state_deserializer_ = nullptr;
    layer_tree_host_in_process_ = nullptr;
}

void LayerTreeHostRemoteForTesting::SetVisible(bool visible)
{
    LayerTreeHostRemote::SetVisible(visible);

    // We need to tell the InProcessHost about visibility changes as well.
    layer_tree_host_in_process_->SetVisible(visible);
}

void LayerTreeHostRemoteForTesting::SetCompositorFrameSink(
    std::unique_ptr<CompositorFrameSink> compositor_frame_sink)
{
    // Pass through to the InProcessHost since this should be called only in
    // response to a request made by it.
    layer_tree_host_in_process_->SetCompositorFrameSink(
        std::move(compositor_frame_sink));
}

std::unique_ptr<CompositorFrameSink>
LayerTreeHostRemoteForTesting::ReleaseCompositorFrameSink()
{
    // Pass through to the InProcessHost since that holds the CompositorFrameSink.
    return layer_tree_host_in_process_->ReleaseCompositorFrameSink();
}

void LayerTreeHostRemoteForTesting::SetNeedsRedrawRect(
    const gfx::Rect& damage_rect)
{
    // Draw requests pass through to the InProcessHost.
    layer_tree_host_in_process_->SetNeedsRedrawRect(damage_rect);
}

void LayerTreeHostRemoteForTesting::SetNextCommitForcesRedraw()
{
    // Draw requests pass through to the InProcessHost.
    layer_tree_host_in_process_->SetNextCommitForcesRedraw();
}

void LayerTreeHostRemoteForTesting::NotifyInputThrottledUntilCommit()
{
    // Pass through because the InProcessHost has the input handler.
    layer_tree_host_in_process_->NotifyInputThrottledUntilCommit();
}

const base::WeakPtr<InputHandler>&
LayerTreeHostRemoteForTesting::GetInputHandler() const
{
    return layer_tree_host_in_process_->GetInputHandler();
}

void LayerTreeHostRemoteForTesting::Initialize(
    TaskGraphRunner* task_graph_runner,
    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
    scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner,
    std::unique_ptr<FakeImageSerializationProcessor>
        image_serialization_processor)
{
    image_serialization_processor_ = std::move(image_serialization_processor);
    RemoteCompositorBridgeImpl* remote_compositor_bridge_impl = static_cast<RemoteCompositorBridgeImpl*>(remote_compositor_bridge());
    remote_compositor_bridge_impl->SetRemoteHost(this);

    animation_host_ = AnimationHost::CreateForTesting(ThreadInstance::MAIN);
    layer_tree_host_in_process_ = CreateLayerTreeHostInProcess(
        layer_tree_host_in_process_client_.get(), task_graph_runner,
        GetSettings(), main_task_runner, impl_task_runner, animation_host_.get());

    compositor_state_deserializer_ = base::MakeUnique<CompositorStateDeserializer>(
        layer_tree_host_in_process_.get(),
        image_serialization_processor_->CreateClientPictureCache(), this);

    // Override the LayerFactory since a lot of tests rely on the fact that Layers
    // and LayerImpls have matching ids.
    compositor_state_deserializer_->SetLayerFactoryForTesting(
        base::MakeUnique<RemoteClientLayerFactory>());

    // Override the TaskRunnerProvider since tests may rely on accessing the impl
    // task runner using it.
    SetTaskRunnerProviderForTesting(
        TaskRunnerProvider::Create(main_task_runner, impl_task_runner));
}

std::unique_ptr<LayerTreeHostInProcess>
LayerTreeHostRemoteForTesting::CreateLayerTreeHostInProcess(
    LayerTreeHostClient* client,
    TaskGraphRunner* task_graph_runner,
    const LayerTreeSettings& settings,
    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
    scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner,
    MutatorHost* mutator_host)
{
    LayerTreeHostInProcess::InitParams params;

    params.client = client;
    params.task_graph_runner = task_graph_runner;
    params.settings = &settings;
    params.main_task_runner = main_task_runner;
    params.mutator_host = mutator_host;

    return LayerTreeHostInProcess::CreateThreaded(impl_task_runner, &params);
}

void LayerTreeHostRemoteForTesting::DidUpdateLocalState()
{
    client_state_dirty_ = true;
}

void LayerTreeHostRemoteForTesting::DispatchDrawAndSubmitCallbacks()
{
    // Don't dispatch callbacks right after the commit on the remote host. Since
    // tests rely on CompositorFrames being swapped on the CompositorFrameSink,
    // we wait for these callbacks from the LayerTreeHostInProcess.
}

void LayerTreeHostRemoteForTesting::RemoteHostNeedsMainFrame()
{
    layer_tree_host_in_process_->SetNeedsAnimate();
}

void LayerTreeHostRemoteForTesting::BeginRemoteMainFrame()
{
    // Send scroll/scale updates first if modified on the impl thread.
    if (client_state_dirty_) {
        client_state_dirty_ = false;
        proto::ClientStateUpdate client_state_update;
        compositor_state_deserializer_->PullClientStateUpdate(&client_state_update);
        ApplyStateUpdateFromClient(client_state_update);

        // Tell the host on the client that the updates were applied to the state
        // on the remote host, in case the main frame on the remote host is aborted.
        compositor_state_deserializer_->DidApplyStateUpdatesOnEngine();
    }

    BeginMainFrame();
}

void LayerTreeHostRemoteForTesting::ProcessRemoteCompositorUpdate(
    std::unique_ptr<CompositorProtoState> compositor_proto_state)
{
    DCHECK(layer_tree_host_in_process_->CommitRequested());

    // Deserialize the update from the remote host into client side LTH in
    // process. This bypasses the network layer.
    const proto::LayerTreeHost& layer_tree_host_proto = compositor_proto_state->compositor_message->layer_tree_host();
    compositor_state_deserializer_->DeserializeCompositorUpdate(
        layer_tree_host_proto);

    const proto::LayerUpdate& layer_updates = compositor_proto_state->compositor_message->layer_tree_host()
                                                  .layer_updates();
    for (int i = 0; i < layer_updates.layers_size(); ++i) {
        int engine_layer_id = layer_updates.layers(i).id();
        Layer* engine_layer = GetLayerTree()->LayerById(engine_layer_id);
        Layer* client_layer = compositor_state_deserializer_->GetLayerForEngineId(engine_layer_id);

        // Copy test only layer data that are not serialized into network messages.
        // So in test cases, layers on the client have the same states as their
        // corresponding layers on the engine.
        client_layer->SetForceRenderSurfaceForTesting(
            engine_layer->force_render_surface_for_testing());
    }

    // The only case where the remote host would give a compositor update is if
    // they wanted the main frame to go till the commit pipeline stage. So
    // request one to make sure that the in process main frame also goes till
    // the commit step.
    layer_tree_host_in_process_->SetNeedsCommit();
}

} // namespace cc
